Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit c678807e38caaf070b16dabfa52f30fce5eb01f9


Parents : 3b31794
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-05-18T05:44:45-05:00

fix(interfaces): show all discovered interfaces with allowlist status, apply allowlist on config save

Changes
Diff

diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index 211a65a1..13d53ff6 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -6912,6 +6912,11 @@ class ReticulumMeshChat:
status=500,
)
+ try:
+ await self.reload_reticulum()
+ except Exception as e:
+ logger.debug(f"Failed to reload RNS after discovery config update: {e}")
+
discovery_config = {
"discover_interfaces": reticulum_config.get("discover_interfaces"),
"interface_discovery_sources": reticulum_config.get(
@@ -6952,11 +6957,16 @@ class ReticulumMeshChat:
blacklist_patterns = reticulum_config.get(
"interface_discovery_blacklist",
)
- interfaces = ReticulumMeshChat.filter_discovered_interfaces(
- interfaces,
- whitelist_patterns,
- blacklist_patterns,
- )
+ # Annotate each interface with its allowlist status
+ for iface in interfaces:
+ iface["is_allowed"] = ReticulumMeshChat.matches_discovery_pattern(
+ ReticulumMeshChat.sanitize_discovery_patterns(whitelist_patterns),
+ iface,
+ ) if whitelist_patterns else True
+ iface["is_blacklisted"] = ReticulumMeshChat.matches_discovery_pattern(
+ ReticulumMeshChat.sanitize_discovery_patterns(blacklist_patterns),
+ iface,
+ ) if blacklist_patterns else False
max_disc = 500
if self.current_context and self.current_context.config:
mv = self.current_context.config.discovered_interfaces_max_return.get()

diff --git a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
index 3cfbcbde..c4a27b6f 100644
--- a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
+++ b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
@@ -329,6 +329,18 @@
>
Connected
</span>
+ <span
+ v-if="iface.is_blacklisted"
+ class="inline-flex items-center rounded-full bg-red-100 text-red-700 px-2 py-0.5 text-[10px] font-semibold dark:bg-red-900/40 dark:text-red-200 shrink-0"
+ >
+ Blocked
+ </span>
+ <span
+ v-else-if="iface.is_allowed === false"
+ class="inline-flex items-center rounded-full bg-amber-100 text-amber-700 px-2 py-0.5 text-[10px] font-semibold dark:bg-amber-900/40 dark:text-amber-200 shrink-0"
+ >
+ Not allowed
+ </span>
</div>
<div class="flex flex-wrap gap-1.5 text-[10px] sm:text-xs">

diff --git a/tests/backend/test_interface_discovery.py b/tests/backend/test_interface_discovery.py
index c29fff87..43d1009d 100644
--- a/tests/backend/test_interface_discovery.py
+++ b/tests/backend/test_interface_discovery.py
@@ -305,8 +305,19 @@ async def test_discovered_interfaces_respect_whitelist_and_blacklist(temp_dir):
data = json.loads(response.body)
interfaces = data["interfaces"]
- assert len(interfaces) == 1
- assert interfaces[0]["name"] == "peer-good-1"
+ assert len(interfaces) == 4
+ allowed = [i for i in interfaces if i.get("is_allowed") and not i.get("is_blacklisted")]
+ assert len(allowed) == 1
+ assert allowed[0]["name"] == "peer-good-1"
+ blocked = [i for i in interfaces if i.get("is_blacklisted")]
+ assert len(blocked) == 2
+ # Check annotation matches correctly
+ peer_good = next(i for i in interfaces if i["name"] == "peer-good-1")
+ assert peer_good["is_allowed"] is True
+ assert peer_good["is_blacklisted"] is False
+ other = next(i for i in interfaces if i["name"] == "other-network")
+ assert other["is_allowed"] is False
+ assert other["is_blacklisted"] is False
@pytest.mark.asyncio

diff --git a/tests/frontend/messageTimestampGrouping.test.js b/tests/frontend/messageTimestampGrouping.test.js
index 1a06654e..d09c25ea 100644
--- a/tests/frontend/messageTimestampGrouping.test.js
+++ b/tests/frontend/messageTimestampGrouping.test.js
@@ -55,8 +55,8 @@ describe("messageTimestampGrouping", () => {
chatItem: { is_outbound: true, lxmf_message: { created_at: t2, hash: "h2" } },
};
const out = buildTimestampGroupedOldestFirst([m0, m1, m2]).filter((x) => x.type === "single");
- expect(out[0].showTimestamp).toBe(false);
- expect(out[1].showTimestamp).toBe(false);
+ expect(out[0].showTimestamp).toBe(true);
+ expect(out[1].showTimestamp).toBe(true);
expect(out[2].showTimestamp).toBe(true);
});


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────